建立 lib/gratitude_web/live/note_live/form_component.ex
並填入:
defmodule GratitudeWeb.NoteLive.FormComponent do
use GratitudeWeb, :live_component
alias Gratitude.Notes
alias Gratitude.Notes.Note
def update(_params, socket) do
form =
%Note{}
|> Note.changeset(%{})
|> to_form()
{:ok, assign(socket, form: form)}
end
def render(assigns) do
~H"""
<div>
<.header>
新增感激筆記
</.header>
<.simple_form for={@form} phx-target={@myself} phx-change="validate" phx-submit="save">
<.input field={@form[:content]} type="text" label="內容" />
<:actions>
<.button>送出</.button>
</:actions>
</.simple_form>
</div>
"""
end
end
從 update
開始,這裡與先前在 Controller-View 的步驟很像,先產生一個新增 note 的 changeset,再放到 assigns 裡面提供給表格,不過在 LiveView 這邊推薦先使用 to_form
轉換成對 LiveView 更新畫面更有效率的 Phoenix.HTML.Form
struct。
一樣是由 CoreComponent 提供的.simple_form
component 裡面用的一樣是 <.form>
使用方式一樣,只是多了造型與 <:action>
slot 提供擺放按鈕的位置。
要注意的是,在 LiveView 中,<.form>
上使用 phx-change
來即時檢查表單的輸入,phx-submit
來處理表單送出的事件。
這兩個都是使用 handle_event 來接收。
(由於是要發給 component 自己,form 上面必須要加入 phx-target={@myself}
,否則事件會發到父層的 LiveView)
在 LiveView 預設每次表單有改變,就會發出 phx-change
事件給 LiveView,所以我們只要在 handle_event
裡面接收即可。
def handle_event("validate", %{"note" => note_params}, socket) do
form =
%Note{}
|> Note.changeset(note_params)
|> Map.put(:action, :validate)
|> to_form()
{:noreply, assign(socket, form: form)}
end
當我們使用 handle_event 接收相對應的事件時(這裡是 "validate" ),可以在 params 內得到表單目前的內容,在這邊把 params 傳到 changeset 裡面,並且把 action 設為 :validate
,<.form>
就會顯示目前的表格的錯誤欄位與原因。
如果按下送出按鈕,就會發出 phx-submit
事件,我們可以實作另一個 handle_event 來接收這個事件,並且在這裡處理儲存的邏輯。
與在 Controller-View 裡的 create 函式一樣,使用我們在 context 寫好的 Notes.create_note
錯誤的話,就把錯誤的 changeset 傳回表單,讓使用者可以修改,成功的話有幾件事要處理:
put_flash
來顯示成功的訊息push_patch
回到 /
與 :index
live_action,這會關閉目前的表格(在上一篇有設定 live_action 是 :new
的話才開啟表格)send
來發送訊息給 LiveView,讓它更新列表。def handle_event("save", %{"note" => note_params}, socket) do
case Notes.create_note(note_params) do
{:ok, _note} ->
send(self(), :saved)
{:noreply,
socket
|> put_flash(:info, "新增成功")
|> push_patch(to: ~p"/")}
{:error, changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end
回到 lib/gratitude_web/live/note_live/index.ex
加入新的 handle_info 來接收 {:saved, note}
訊息,並重新讀取新的列表。
def handle_info(:saved, socket) do
{:noreply, socket |> assign(:notes, Notes.list_notes())}
end
更新了 assigns 裡的 notes 之後畫面會自動更新
最後在 <.modal>
裡替換成剛剛建立的 FormComponent 。
<.modal :if={@live_action in [:new, :edit]} id="book-modal" show on_cancel={JS.patch(~p"/")}>
<.live_component
module={GratitudeWeb.NoteLive.FormComponent}
id="new_note_form"
action={@live_action}
/>
</.modal>